//
// Created by wang on 2021/6/29.
//
#include <string.h>
#include "arp.h"
#include "ipvx.h"

trans_entry *g_trans_tbl = NULL;

static void add_trans_entry(struct in_addr addr, mac m);
static trans_entry *find_trans_entry(struct in_addr addr);
static void delete_trans_entry(trans_entry *e);
static void delete_all_trans();

static void update_trans_entry(struct in_addr addr, mac m) {
    trans_entry *e;

    /* addr already in the hash? */
    HASH_FIND_INT(g_trans_tbl, &TO_ARP_H_KEY(addr), e);
    if (e) {
        memcpy(e->m, m, sizeof(mac));
    }
}

static void add_trans_entry(struct in_addr addr, mac m) {

    trans_entry *e;

    /* addr already in the hash? */
    HASH_FIND_INT(g_trans_tbl, &TO_ARP_H_KEY(addr), e);
    if (e == NULL) {
        e = (trans_entry *)malloc(sizeof *e);
        if (!e) {
            /* TODO: log this error */
            return;
        }
        e->ARP_H_KEY = addr.s_addr;
        /* ARP_H_KEY: name of key field */
        HASH_ADD_INT(g_trans_tbl, ARP_H_KEY, e);
    }
    memcpy(e->m, m, sizeof(mac));
}

static trans_entry *find_trans_entry( struct in_addr addr) {
    trans_entry *e;

    /* e: output pointer */
    HASH_FIND_INT(g_trans_tbl, &TO_ARP_H_KEY(addr), e);
    return e;
}

static void delete_trans_entry(trans_entry *e) {

    /* e: pointer to delete */
    HASH_DEL(g_trans_tbl, e);
    free(g_trans_tbl);
}

static void delete_all_trans() {
    trans_entry *current_e, *tmp;

    HASH_ITER(hh, g_trans_tbl, current_e, tmp) {
        /* delete it (addr advances to next) */
        HASH_DEL(g_trans_tbl, current_e);
        free(current_e); /* free it */
    }
}

ret_t arp_in(rx_chain *chain) {

    arp_header arp_h;
    int8_t merge = false;
    int32_t tbl_idx;
    struct in_addr *sender_ip = NULL;
    mac sender_mac;
    ip_node m_ip;
    memset(&arp_h, 0, sizeof arp_h);

    memcpy(&arp_h, chain->rx, sizeof arp_h);
    if (ntohs(arp_h.hrd) != HW_TYPE_ETHERNET)
        return ARP_RET_ERROR;

    if (ntohs(arp_h.pro) != IP_PROTOCOL_V4)
        return ARP_RET_ERROR;

    sender_ip = SENDER_IP(chain, arp_h);
    memcpy(sender_mac, SENDER_MAC(chain), sizeof sender_mac);
    merge = false;

    /* Search translation table */
    trans_entry *en = find_trans_entry(*sender_ip);

    if (en) {
        /* Update the pair <ip_addr, mac> */
        update_trans_entry(*sender_ip, sender_mac);
    }

    /* Am I the target protocol address? */
    ipvx_get_ip_node(&m_ip);
    if (NODE_CMP_IP(m_ip, *sender_ip)) {
        if (false == merge) {
            add_trans_entry(*sender_ip, sender_mac);
        }

        if (ARP_OP_REQUEST == ntohs(arp_h.op)) {
            /* Send REPLY */
        }
    }

    return 0;
}

ret_t arp_send_reply(
        struct in_addr dst_ip, mac dst_mac) {

    arp_header header;
}
